#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <cm/character/character.h>
#include <cm/character/lattice.h>
#include <cm/img/cm_yuv420sp.h>
#include <cm/img/cm_yuv422p.h>
#include <cm/img/cm_rgb24.h>

#define H_SPACE 2
#define V_SPACE 2

const int HZK_STEP_X = HZK16_COL + H_SPACE;
const int HZK_STEP_Y = HZK16_ROW + V_SPACE;
const int ASC_STEP_X = ASC16_COL + H_SPACE;
const int ASC_STEP_Y = ASC16_ROW + V_SPACE;

/* @brief yuv422p格式像素点着色
 * @param i,j表示像素点位置(i,j) param是着色参数
 */
static inline void do_draw_pixel_422p(int i, int j,
        struct draw_lattice_param *param)
{
    int y, u, v;
    y = yuv422p_y_offset(i, j, param->w, param->h);
    u = yuv422p_u_offset(i, j, param->w, param->h);
    v = yuv422p_v_offset(i, j, param->w, param->h);

    if (-1 == y || -1 == u || -1 == v)
        return;

    param->img[y] = param->color.yuv.y;
    param->img[u] = param->color.yuv.u;
    param->img[v] = param->color.yuv.v;
}

/* @brief yuv420sp格式像素点着色
 * @param i,j表示像素点位置(i,j) param是着色参数
 */
static inline void do_draw_pixel_420sp(int i, int j,
        struct draw_lattice_param *param)
{
    int y, uv;
    y = yuv420sp_y_offset(i, j, param->w, param->h);
    uv = yuv420sp_uv_offset(i, j, param->w, param->h);

    if (-1 == y || -1 == uv)
        return;

    param->img[y] = param->color.yuv.y;
    param->img[uv] = param->color.yuv.u;
    param->img[uv + 1] = param->color.yuv.v;
}

/* @brief yuv420sp格式像素点着色
 * @param i,j表示像素点位置(i,j) param是着色参数
 */
static inline void do_draw_pixel_rgb24(int i, int j,
        struct draw_lattice_param *param)
{
    int r, g, b;
    r = rgb24_r_offset(i, j, param->w, param->h);
    if (-1 == r)
        return;
    g = r + 1;
    b = r + 2;

    param->img[r] = param->color.rgb.r;
    param->img[g] = param->color.rgb.g;
    param->img[b] = param->color.rgb.b;
}

/* @brief 画某个具体的像素点 */
static void do_draw_pixel(struct draw_lattice_param *param)
{
    int i, j;
    int x, y;

    x = param->x * param->scale;
    y = param->y * param->scale;

    void (*pfunc)(int, int, struct draw_lattice_param*);

    switch (param->type) {
    case CM_COLOR_YUV420SP:
        pfunc = do_draw_pixel_420sp;
        break;
    case CM_COLOR_YUV422P:
        pfunc = do_draw_pixel_422p;
        break;
    case CM_COLOR_RGB24:
        pfunc = do_draw_pixel_rgb24;
        break;
    default:
        return;
    }

    for (i = x; i < x + param->scale; i++) {
        for (j = y; j < y + param->scale; j++) {
            pfunc(i, j, param);
        }
    }
}

/* @brief 点阵处理中的回调函数，在遍历点阵中的点时被调用
 * @param row,col表示当前遍历到点阵中某一点所在的行和列
 *        seted表示该点的值(1个bit)是否被置位
 *        arg是外部传入的参数，这里是打字参数
 */
static inline void draw_seted_pixel(int row, int col, int seted, void *arg)
{
    if (!seted)
        return;
    struct draw_lattice_param param = *(struct draw_lattice_param *) arg;
    param.x += col;
    param.y += row;

    do_draw_pixel(&param);
}

/*
 * do_draw_asc 与 do_draw_hz的作用：对点阵中的每一点调用draw_seted_pixel函数
 */

static inline void do_draw_asc(struct draw_lattice_param *param,
        char latc[ASC16_BYTE_PER_CHAR])
{
    asc_foreach(latc, draw_seted_pixel, param);
}

static inline void do_draw_hzk(struct draw_lattice_param *param,
        char latc[HZK16_BYTE_PER_CHAR])
{
    hzk_foreach(latc, draw_seted_pixel, param);
}

/* [FUNC]   draw_lattice_yuv420sp
 * [DESC]   将以GB2312编码的字符串打印到图像中
 * [RETURN] none
 */
void draw_lattice(const struct draw_lattice_param *param)
{
    char latc_hzk[HZK16_BYTE_PER_CHAR];
    char latc_asc[ASC16_BYTE_PER_CHAR];

    struct draw_lattice_param parg = *param;

    const unsigned char *ptr = (unsigned char *) (parg.str);
    while (*ptr != 0) {
        if (*ptr == '\n') {
            parg.y += HZK_STEP_Y;
            parg.x = param->x;
            ptr++;
        } else if (*ptr <= 0x7F) {
            if (0 == asc16_get(*ptr, latc_asc)) {
                do_draw_asc(&parg, latc_asc);
                parg.x += ASC_STEP_X;
                ptr++;
            } else {
                printf("get asc16 lattice failed.\n");
                break;
            }
        } else {
            if (0 == hzk16_get((struct gb2312_char*) ptr, latc_hzk)) {
                do_draw_hzk(&parg, latc_hzk);
                parg.x += HZK_STEP_X;
                ptr += 2;
            } else {
                printf("get hzk16 lattice failed.\n");
                break;
            }
        }
    }
}

